package NWPackage;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

import NW_EM.EM_Borda;
import NW_EM.EM_Borda_Second_Winner;
import NW_Hybrid.Hybrid;
import NW_MC.FullMonteCarlo;
import NW_MC.MonteCarlo;
import NW_Random.MyRandom;
import Readers.ExcelInputs;
import Readers.ExperimentSkewnessExcelInputs;
import Readers.ExperimentsReader;
import Readers.InputsReader;
import Readers.SkewnessInput;
import Readers.StaticInputs;
import Writers.MsgLog;

public class RunExperiment {
	
	private long startInitMatrixTime;
	private long endInitMatrixTime;
	private long startAlgoTime;
	private long endAlgoTime;
//	private double total_queries;
	private long avg_init_prob_time;
	private long avg_algo_time;
	private int queryFinderAlg;
	public QueryFinderAlg nextQueryFinder;
	private int randomCeedNumber = 1;
	private int MCSeedNumber=1;
	private int em_seed = 1;
	//private double avg_right_guesses=0;
	private int LOOPS = 10;
	private double randomPrecentage = 0.0;
	private SkewnessInput skewnessReader = null;
	ExperimentsReader expreader; //reads and logs exps
	int expIndex; //shows at which exp we are
	
	public String[] get_args_from_user() throws Exception{
		String[] myArgs = new String[9];
	//	String exit = "";
		queryFinderAlg = 1;
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));		        
        System.out.println("please enter the skewness input file name (.xls/x)");
		myArgs[0] = br.readLine();
		System.out.println();				 
		System.out.println("A. name of expermiment file: ");
		myArgs[1] = br.readLine();
		System.out.println("B. number of experiments to run: ");
		myArgs[2] = br.readLine();			
		System.out.println("C. choose Heuristic: 1 - By Winner's Entropy; 2 - Winner VS Loser; 3 - Second Winner; 4 - Entropy Only ; 5 - Random heristic ; 6 - Myopic probs");
		myArgs[3] = br.readLine();
		if (Integer.parseInt(myArgs[3])!=5){
			System.out.println("D. monte carlo iterations - ('0' Input for Random alg)");
			myArgs[4] = br.readLine();
			System.out.println("E. use Information Gain? - (Y/N)");
			myArgs[6] = br.readLine();
			System.out.println("F. Precentage of Random Queries (default '0')");
			myArgs[5] = br.readLine();			
			}
		else{
			myArgs[4] = "0";
			myArgs[5] = "0";
			}	
		return myArgs;
	}
	
	 
	
	public void treat_args(String[] myArgs)throws Exception{						
		LOOPS =(int) Double.parseDouble(myArgs[2]);				
		if (myArgs[6] != null){
			if (myArgs[6].toLowerCase().equals("y"))
				InputsReader.IGUsing = true;
			else
				InputsReader.IGUsing = false;
		}						
		//random exp
		if(	myArgs[3].equals("5")){
			System.out.println("random");
			queryFinderAlg = 2; //Random
		}		
		else if (myArgs[3].equals("10")){
			System.out.println("EM_Borda");
			queryFinderAlg = 55;  //EM_Borda
		}
			else if (myArgs[3].equals("11")){
				System.out.println("EM-Borda2");
				queryFinderAlg = 6;			
			}
			    else  {
			    	System.out.println("Monte Carlo");
				    queryFinderAlg = 1; //Monte Carlo
			    }
	}
	
	
	public void run_all_exps(String expfilename) throws Exception{
		expreader = new ExperimentsReader(expfilename);
		int amount = expreader.getAmountOfExperiments();
		System.out.println("total of " + amount + " experiments");
		for(int i=1; i<amount+1; i++){  //the first row contains headers
			expIndex = i;
			String myArgs[] = expreader.read_experiment_at_row(i);
			System.out.println("exp #" + i);
			run_exp(myArgs);
		}		
	}
	
	//lihi's new
	public void run_exp(String myArgs[]) throws Exception{				
		boolean prob_flag = true;	
		if ((myArgs[3].equals("10"))||(myArgs[3].equals("5"))||(myArgs[3].equals("11")))
			prob_flag = false; //no need for probs
		else 
			prob_flag = true;
		if (myArgs[7].equals("sushi")){
			if(!prob_flag)
				System.out.println("skipping the skewness part");  //we don't need them, no probs necessary							
			else										
				skewnessReader = new SkewnessInput(myArgs[0]);
			int a = Integer.parseInt(myArgs[3]);
			int b = (int)Double.parseDouble(myArgs[4]);			 
			InputsReader.inputs = new ExperimentSkewnessExcelInputs(myArgs[1],a,b,skewnessReader, prob_flag);
		}//if sushi
		else{
			if (myArgs[7].equals("netflix")){
				InputsReader.inputs = new ExcelInputs(myArgs[0],myArgs[1],Integer.parseInt(myArgs[3]),Integer.parseInt(myArgs[4]));
			}
			else
				throw new Exception ("no such experiment type");
		}//else
		treat_args(myArgs);	
		//----------------------------------------------------------------------------		
		//Initial the Calculation Class
		Calculations.numAgents = InputsReader.getNumAgents();
		Calculations.numCandidates = InputsReader.getNumCandidates();
		ProbabilitiesMatrix probMatrix = null;
		if(prob_flag){
			probMatrix = InitProbMatrix();
		}
		RealVoteMatrix realVoteMatrix = InitRealVoteMatrix();			
		//initial the protocol algorithm (Borda)
		Protocol protocol = new BordaProtocol();
		protocol.Initial(InputsReader.getCandidates());				
//----------------------------------------------------------------------------
		chooseQueryFinderAlg();		
//----------------------------------------------------------------------------		 
		MsgLog.write_to_log("file= " +myArgs[0]+" "+ myArgs[1] +" Algorithm= " + myArgs[3] + " MC= " + myArgs[4] + " exp_amount= " + myArgs[2]);		 				 							
		Loop(probMatrix,realVoteMatrix,protocol,LOOPS, prob_flag);		
		
	}
	
/*	//inon's old
	public void run_exp(String[] myArgs, String exptype) throws Exception {				
		boolean prob_flag = true;
		
		if ((myArgs[3] == "10")||(myArgs[3] == "5")||(myArgs[3] == "11"))
			prob_flag = false; //no need for probs
		if (exptype == "netflix"){			
			//Initial the Data Reader			 
			InputsReader.inputs = new ExcelInputs(myArgs[0],myArgs[1],Integer.parseInt(myArgs[3]),Integer.parseInt(myArgs[4]));
			//InputsReader.inputs = new StaticInputs();  //Lihi says: I don't know what this is. perhaps for debugging?
			
			
		}		
		else if (exptype == "sushi"){
			if(!prob_flag){
				System.out.println("skipping the skewness part");  //we don't need them, no probs necessary				
			}
			else{				
				try {				
					skewnessReader = new SkewnessInput(myArgs[0]);
					} catch (IOException e) {
					e.getMessage();
					System.exit(1);
				}				
			}
			InputsReader.inputs = new ExperimentSkewnessExcelInputs(myArgs[1],Integer.parseInt(myArgs[3]),Integer.parseInt(myArgs[4]),skewnessReader, prob_flag);
		}
		else
			throw new Exception ("Error in RunExperiment: no such experiment type");
		
		treat_args(myArgs);											
//----------------------------------------------------------------------------		
				//Initial the Calculation Class
				Calculations.numAgents = InputsReader.getNumAgents();
				Calculations.numCandidates = InputsReader.getNumCandidates();
				ProbabilitiesMatrix probMatrix = null;
				if(prob_flag){
					probMatrix = InitProbMatrix();
				}
				RealVoteMatrix realVoteMatrix = InitRealVoteMatrix();			
				//initial the protocol algorithm (Borda)
				Protocol protocol = new BordaProtocol();
				protocol.Initial(InputsReader.getCandidates());				
//----------------------------------------------------------------------------
				chooseQueryFinderAlg();		
//----------------------------------------------------------------------------		 
				MsgLog.write_to_log("file= " +myArgs[0]+" "+ myArgs[1] +" Algorithm= " + myArgs[3] + " MC= " + myArgs[4] + " exp_amount= " + myArgs[2]);		 				 							
				Loop(probMatrix,realVoteMatrix,protocol,LOOPS, prob_flag);			 
	}*/
	
	
	
	private double NecessaryWinnerAlg(ProbabilitiesMatrix probMatrix,RealVoteMatrix realVoteMatrix,Protocol protocol,boolean flag) throws Exception{
		double queries_amount = 0;
		try{
			//MsgLog.write("Starting Algorithm");
			startAlgoTime = System.currentTimeMillis();
			try{
			Algorithms.NecessaryWinnerAlgorithm(protocol,probMatrix,realVoteMatrix,nextQueryFinder,flag);
			}
			catch (Exception e){
				System.out.println("RunExperiment: exception in NecessaryWinnerAlg" );
				e.printStackTrace();
			}
			endAlgoTime = System.currentTimeMillis();
			//MsgLog.write("End Of Algorithm");
			
			//print the answer:
			//MsgLog.write("Number of queries Asked: " + Algorithms.askedQueries.size());
			//System.out.println("Number of queries Asked: " + Algorithms.askedQueries.size());
			queries_amount = Algorithms.askedQueries.size();
			
			if (!Algorithms.necessaryWinners.isEmpty()){
				System.out.println("The Necessary Winner Item is: " + Algorithms.necessaryWinners.get(0));
				//MsgLog.write("The Necessary Winner Item is: " + Algorithms.necessaryWinners.get(0));
				//System.out.println("Time to Calc Init Matrix: " + String.valueOf(endInitMatrixTime-startInitMatrixTime) + "ms");
				avg_init_prob_time += (endInitMatrixTime-startInitMatrixTime);
				
				System.out.println("Algorithm Time: " + String.valueOf(endAlgoTime-startAlgoTime) + "ms");
				avg_algo_time += (endAlgoTime-startAlgoTime);
				
				//double avarage = (double) Algorithms.numberOfRightGuesses/Algorithms.askedQueries.size();
				//System.out.println("Avarage Number Of Guesses: " + avarage );
			}
			else{
				System.out.println("Cannot find Necessary Winner Item!");
				MsgLog.write_to_log("Error in finding Necessary Winner Item!" +  "\n");
			}
		
		}
		catch(Exception exp)
		{
			System.out.println(exp.toString());
			MsgLog.write_to_log(exp.toString());
		}
		return queries_amount;
	}

//Debug-------------------------------------------------//
	private void Loop(ProbabilitiesMatrix probMatrix,RealVoteMatrix realVoteMatrix,Protocol protocol,int loops,boolean flag )throws Exception {
		double total_queries = 0.00;
		avg_init_prob_time = 0;
		avg_algo_time = 0;
		double [] queryList = new double[loops];
		for (int i = 0; i < loops; i++) {
			//MsgLog.write_to_log("Loop Number: " + i);			
			double queriesPerLoop = NecessaryWinnerAlg(probMatrix,realVoteMatrix,protocol,flag);
			total_queries = total_queries + queriesPerLoop;
			queryList[i] = queriesPerLoop; 
			//init all Matrixs
			Calculations.numAgents = InputsReader.getNumAgents();
			Calculations.numCandidates = InputsReader.getNumCandidates();
			if (flag)
			   probMatrix = InitProbMatrix();
			realVoteMatrix = InitRealVoteMatrix();
			protocol = new BordaProtocol();
			protocol.Initial(InputsReader.getCandidates());
			
			chooseQueryFinderAlg();
			MsgLog.write_to_log_new_line("loop = " + i + " queries = " + queriesPerLoop);
		}
		//--Logs and Debugs------------print all the Averages Numbers:----------------//
		double avg_queries = (double)total_queries/(double)loops;
		//System.out.println("Average Number Of Queries: " + avg_queries);		
		avg_init_prob_time = avg_init_prob_time/loops;
		//System.out.println("Average Initial Time: " + avg_init_prob_time);
		//MsgLog.write("Average Initial Time: " + avg_init_prob_time);
		avg_algo_time = avg_algo_time/loops;
		double stdev = calc_stdev(queryList,avg_queries );
		System.out.println("Average Number Of Queries: " + avg_queries+ " Average Algorithm Time: " + avg_algo_time);
		this.expreader.write_to_expFile(expIndex, avg_queries, stdev, avg_algo_time);
		MsgLog.write_to_log_new_line(" avq#Q= " + avg_queries + " avgTime= " + avg_algo_time);
		//----------------------------------------------------------------------------//
	}
	
	private double calc_stdev(double [] arr, double avg){
		double sum = 0;
		for(int i=0; i<arr.length; i++){
			sum = sum + Math.pow((arr[i] - avg),2);
		}
		double stdev = Math.sqrt(sum/arr.length);
		return stdev;
	}

	private ProbabilitiesMatrix InitProbMatrix() {
		//read from excel or from static inputs
		//startInitMatrixTime = System.currentTimeMillis();
		ProbabilityCell[] cells = InputsReader.getMatrixProbabilities();
		//endInitMatrixTime = System.currentTimeMillis();
		ProbabilitiesMatrix probMatrix = new ProbabilitiesMatrix();
		probMatrix.initial(cells);
		return probMatrix;
	}
	
	private static RealVoteMatrix InitRealVoteMatrix()
	{
		RealVoteCell[] realVoteCells = InputsReader.getRealVotesMatrix();
		RealVoteMatrix realVoteMatrix = new RealVoteMatrix();
		realVoteMatrix.initial(realVoteCells);
		return realVoteMatrix;
	}
	
	private void chooseQueryFinderAlg(){

		//Initial the Next Query Algorithm - MonteCarlo / FullMonteCarlo
		//Please Choose: -->	
		nextQueryFinder = null;
		switch (queryFinderAlg) {
		case 1:
			nextQueryFinder = new MonteCarlo(MCSeedNumber++);
			break;
		case 2:
			nextQueryFinder = new MyRandom(randomCeedNumber++);
			break;
		case 3:
			nextQueryFinder = new Hybrid(randomPrecentage);
			break;
		case 4:
			nextQueryFinder = new FullMonteCarlo();
			break;
		case 55:
			nextQueryFinder = new EM_Borda(em_seed++);
			break;
		case 6:
			nextQueryFinder = new EM_Borda_Second_Winner(em_seed++);
		default:
			break;
		}
	}

}
